package edu.neu.cs6515.net;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.io.StringReader;
import java.io.StringWriter;
import java.net.Socket;
import java.util.List;

import javax.xml.bind.JAXBContext;
import javax.xml.bind.JAXBException;
import javax.xml.bind.Marshaller;
import javax.xml.bind.Unmarshaller;

import lombok.Getter;

import com.google.common.collect.ImmutableList;
import com.google.java.contract.Ensures;
import com.google.java.contract.Requires;

import edu.neu.cs6515.xml.False;
import edu.neu.cs6515.xml.Tile;
import edu.neu.cs6515.xml.w12.Accept;
import edu.neu.cs6515.xml.w12.End;
import edu.neu.cs6515.xml.w12.PlayerNames;
import edu.neu.cs6515.xml.w12.ReRackPlease;
import edu.neu.cs6515.xml.w12.Register;
import edu.neu.cs6515.xml.w12.Rerack;
import edu.neu.cs6515.xml.w12.Rerackable;
import edu.neu.cs6515.xml.w12.True;
import edu.neu.cs6515.xml.w12.TurnResult;
import edu.neu.cs6515.xml.w12.XMLTurn;

public class ProxySocketInOutput implements IProxyInOutput {


    private final Socket sock;
    @Getter
    private PrintWriter writer = null;
    @Getter
    private BufferedReader reader = null;
    @Getter
    private Marshaller marshaller = null;
    @Getter
    private Unmarshaller unmarshaller = null;
    
    Class[] classes = { Accept.class, TurnResult.class,
            PlayerNames.class, XMLTurn.class, False.class, End.class,
            True.class, Rerack.class, Tile.class, Register.class, Rerackable.class };

    public ProxySocketInOutput(final Socket sock) throws JAXBException, IOException {
        super();
        this.sock = sock;
        JAXBContext xmlContext;
        xmlContext = JAXBContext.newInstance(classes);
        writer = new PrintWriter(sock.getOutputStream());
        reader = new BufferedReader(new InputStreamReader(sock.getInputStream()));
        this.marshaller = xmlContext.createMarshaller();
        marshaller.setProperty(Marshaller.JAXB_FRAGMENT, Boolean.TRUE);
        this.unmarshaller = xmlContext.createUnmarshaller();
    }

    @Override
    public void respond(final Object obj) throws InputOutputException {
        StringWriter sw = new StringWriter();
        try {
            writer.println();
            marshaller.marshal(obj, sw);
            writer.println(sw.toString());
            writer.flush();
        } catch (Exception e) {
            e.printStackTrace();
            throw new InputOutputException("respond marshal failed");
        }
    }

    @Override
    public Object receive() throws InputOutputException {
        try {   
            String message = "";
            String line;
            
            do {
                line = reader.readLine();
                message = message + line;
                if (message.equals("")) {
                    continue;
                } else if (line == null || line.equals("")) {
                    break;
                }
            } while(reader.ready());
                   
            if (!message.equals("")) {
                Object obj = unmarshaller.unmarshal(new StringReader(message));
                return obj;
            } else {
                throw new InputOutputException("end of stream");
            }
        } catch (Exception e) {
            throw new InputOutputException(e.getMessage());
        }
    }
}
